home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / New System Software Extensions / QuickDraw™ GX v1.0ß2 / Sample Code / Printing Samples / Extensions… / Spooling Extension ƒ / Spooling.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-09  |  23.8 KB  |  878 lines  |  [TEXT/MPS ]

  1. /*________________________________________________________
  2.  
  3.     File: Spooling.c
  4.     
  5.     This is the C source for a printing extension
  6.     that redirects spool files.
  7.     
  8.     Dave Hersey
  9.     Apple Developer Technical Support
  10.  
  11.      1/29/93 - dmh - Begat.
  12.      4/26/93 - dmh - Updated to b1.
  13.              - Switched to using the recommended
  14.                approach for initializing global data.
  15.      9/06/93 - dmh - Updated to b2.
  16.              - Switched to Exception.h assertion stuff
  17.                for error checking.
  18.  
  19.     (Note: all functions are in the Mark menu)
  20.     
  21. __________________________________________________________*/
  22.  
  23. #include "Spooling.h"
  24.  
  25. // globals:
  26.  
  27. short        gPrefsVRefNum;            // The volume it resides on.  (For FlushVol.)
  28. AliasHandle    gCurFolderAlias;        // The current folder alias used by the panel.
  29. Boolean        gSettingsChanged;        // True if we have panel changes to save.
  30. Boolean        gLastOnOffSetting;        // The "extTurnedOn" setting when we opened the panel.
  31.  
  32.  
  33. /*******************************************************************
  34.     InitGlobalData is used to initialize any global data we need to
  35.     in our initialize message override.  It's critical that you do
  36.     things this way, rather than access the data in the same scope
  37.     that you call NewMessageGlobals.  Otherwise, some compilers
  38.     will give you code that references an invalid A5 world.
  39.  
  40. ********************************************************************/
  41.  
  42. OSErr InitGlobalData()
  43. {
  44.     gCurFolderAlias = nil;
  45.     return noErr;
  46. }
  47.  
  48.  
  49. /*******************************************************************
  50.     SPInitialize is our override for GXInitialize.  We set up our
  51.     A5 world and jump off to initialize our global data.
  52.  
  53. ********************************************************************/
  54.  
  55. OSErr SPInitialize()
  56. {
  57.     OSErr    err;
  58.     
  59.     err = NewMessageGlobals(A5Size(), A5Init);
  60.     if (!err) err = InitGlobalData();
  61.  
  62.     return err;
  63. }
  64.  
  65.  
  66. /*******************************************************************
  67.     SPShutDown is our override for GXShutDown.  It just frees up
  68.     the A5 world we created in SPInitialize.
  69.  
  70. ********************************************************************/
  71.  
  72. OSErr SPShutDown()
  73. {
  74.     DisposeMessageGlobals();
  75.     return noErr;
  76. }
  77.  
  78.  
  79. /*******************************************************************
  80.     SPJobPrintDialog is our override for GXJobPrintDialog.  All we
  81.     do is set up our panel and then forward the message.
  82.  
  83. ********************************************************************/
  84.  
  85. OSErr SPJobPrintDialog(gxDialogResult *dlogResult)
  86. {
  87.     SetUpPrintPanel();
  88.     return Forward_GXJobPrintDialog(dlogResult);
  89. }
  90.  
  91.  
  92. /*******************************************************************
  93.     SetUpPrintPanel sets up our print panel, adding a default
  94.     SpoolCollection item to the job collection with our previous
  95.     settings.  This collection item has the values we'll use to
  96.     set up our panel's controls.
  97.  
  98. ********************************************************************/
  99.  
  100. OSErr SetUpPrintPanel()
  101. {
  102.     OSErr                    err;
  103.     gxPanelSetupRecord        panelSetupRec;
  104.     SpoolCollection            spoolConfig, lastJobConfig;
  105.     Boolean                    newItem;
  106.  
  107. // Try to find our collection item.  We also try to get the default settings so
  108. // that we make sure the Prefs file is available.  If it's not, a new file will
  109. // be created by GetDefaultSettings.
  110.  
  111.     err = GetDefaultSettings(&spoolConfig);
  112.     nrequire(err, CanNotLoadDefaults);
  113.  
  114.     err = GetSpoolCollection(&lastJobConfig);
  115.     newItem = (err == collectionItemNotFoundErr);
  116.  
  117.  
  118. // Store our settings in the job collection.
  119.  
  120.     if (!err || newItem)
  121.         err = StoreSpoolCollection(&spoolConfig, newItem);
  122.  
  123.     nrequire(err, HaveCollectionMgrError);
  124.  
  125.  
  126. // Now, do the actual panel set up.
  127.  
  128.     gSettingsChanged = false;
  129.     gLastOnOffSetting = spoolConfig.extTurnedOn;
  130.  
  131.     panelSetupRec.panelResId        = r_spoolPanel;            // which panel resource?
  132.     panelSetupRec.resourceRefNum    = GXGetMessageHandlerResFile();    // where is it?
  133.     panelSetupRec.refCon            = 0;                    // we don't use this.
  134.     panelSetupRec.panelKind            = gxExtensionPanel;        // This is an extension panel.
  135.     
  136.     err = GXSetupDialogPanel(&panelSetupRec);
  137.  
  138.  
  139. HaveCollectionMgrError:
  140. CanNotLoadDefaults:
  141.  
  142.     return err;
  143. }
  144.  
  145.  
  146. /*******************************************************************
  147.     SPHandlePanelEvent is our override for GXHandlePanelEvent.  If
  148.     the event is one of ours, we handle it.  Otherwise, we just
  149.     forward it down the chain.
  150.  
  151. ********************************************************************/
  152.  
  153. OSErr SPHandlePanelEvent(gxPanelInfoRecord *panelInfo)
  154. {
  155.     OSErr                        err = noErr;
  156.     GrafPtr                        oldPort;
  157.     DialogPtr                    pDlg;
  158.     Handle                        hItem;
  159.     Rect                        itemRect;
  160.     short                        itemType;
  161.     short                        oldResFile;
  162.     SpoolCollection                spoolConfig;
  163.     FSSpec                        curFSpec;
  164.  
  165.     pDlg = panelInfo->pDlg;
  166.     GetPort(&oldPort);
  167.     SetPort(pDlg);
  168.     
  169.     switch (panelInfo->panelEvt)
  170.     {
  171.  
  172. // If our panel is opening, go do any initialization we need to.
  173.  
  174.         case gxPanelOpenEvt:
  175.             OpenSpoolPanel(pDlg, panelInfo->itemCount);
  176.             break;
  177.  
  178. // If the user hit the "Print" or "Save" buttons, go save any
  179. // changes we need to.
  180.  
  181.         case gxPanelConfirmEvt:
  182.             err = SavePanelChanges();
  183.             break;
  184.  
  185.  
  186. // If the user hits the "Spool to…" button, prompt them for a folder.
  187. // If they select one, get our old collection item, move the FSSpec info to it,
  188. // and replace the old collection item with our modified one.
  189.  
  190.         case gxPanelHitEvt:
  191.             if (panelInfo->itemHit == (panelInfo->itemCount + d_selectDirectory))
  192.             {
  193.                 oldResFile = CurResFile();
  194.                 UseResFile(GXGetMessageHandlerResFile());
  195.  
  196.                 require(GetFolder(&curFSpec), GetFolder_Failed);
  197.                 
  198.                 gSettingsChanged = true;
  199.                 err = GetSpoolCollection(&spoolConfig);
  200.                 nrequire(err, GetSpoolCollection_Failed);
  201.  
  202.                 if (gCurFolderAlias != nil)
  203.                 {
  204.                     DisposHandle((Handle) gCurFolderAlias);
  205.                     gCurFolderAlias = nil;
  206.                 }
  207.                 
  208.                 err = NewAlias(nil, &curFSpec, &gCurFolderAlias);
  209.                 nrequire(err, NewAlias_Failed);
  210.                 AliasToPathName(gCurFolderAlias, &spoolConfig.folderName, &spoolConfig.volumeName);
  211.                 err = StoreSpoolCollection(&spoolConfig, false);
  212.                 nrequire(err, StoreSpoolCollection_Failed);
  213.  
  214.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_folderName, &itemType, &hItem, &itemRect);
  215.                 SetIText(hItem, &spoolConfig.folderName);
  216.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_volumeName, &itemType, &hItem, &itemRect);
  217.                 SetIText(hItem, &spoolConfig.volumeName);
  218.  
  219.  
  220. StoreSpoolCollection_Failed:
  221. NewAlias_Failed:
  222. GetSpoolCollection_Failed:
  223. GetFolder_Failed:
  224.  
  225.                 UseResFile(oldResFile);
  226.             }
  227.             break;
  228.     }
  229.     
  230.     SetPort(oldPort);
  231.     return err;
  232. }
  233.  
  234.  
  235. /*******************************************************************
  236.     OpenSpoolPanel handles non-'xdtl' item initialization when we
  237.     open our panel.  Note that our items will be offset from
  238.     itemCount.  (So, if we want item #5 in our DITL, we pass
  239.     itemCount +5 to GetDItem.)
  240.  
  241. ********************************************************************/
  242.  
  243. void OpenSpoolPanel(DialogPtr pDlg, short itemCount)
  244. {
  245.     SpoolCollection        spoolConfig;
  246.     Handle                hItem;
  247.     Rect                itemRect;
  248.     short                itemType;
  249.  
  250. // Initialize the current file name displayed, based on the
  251. // settings in our SpoolCollection item.
  252.  
  253.     GetSpoolCollection(&spoolConfig);
  254.     GetDItem(pDlg, itemCount +d_folderName, &itemType, &hItem, &itemRect);
  255.     SetIText(hItem, &spoolConfig.folderName);
  256.     GetDItem(pDlg, itemCount +d_volumeName, &itemType, &hItem, &itemRect);
  257.     SetIText(hItem, &spoolConfig.volumeName);
  258. }
  259.  
  260.  
  261. /*******************************************************************
  262.     SavePanelChanges saves our panel settings to disk when they
  263.     change and the user confirms our panel.  We only save the
  264.     settings if they've changed since the last save.
  265.  
  266. ********************************************************************/
  267.  
  268. OSErr SavePanelChanges()
  269. {
  270.     OSErr                err;
  271.     SpoolCollection        spoolConfig, **spoolCollHdl;
  272.     short                oldResFile, resRefNum;
  273.  
  274.     err = GetSpoolCollection(&spoolConfig);
  275.     nrequire(err, GetSpoolCollection_Failed);
  276.  
  277.  
  278. // If our settings haven't changed, exit.
  279.  
  280.     gSettingsChanged = (gSettingsChanged || (spoolConfig.extTurnedOn != gLastOnOffSetting));
  281.     require(gSettingsChanged, SettingsHaveNotChanged);
  282.  
  283.  
  284. // Open the preferences file and set it as our current resource file.
  285.  
  286.     oldResFile = CurResFile();
  287.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  288.     nrequire(err, OpenPrefsFile_Failed);
  289.     UseResFile(resRefNum);
  290.  
  291.  
  292. // Create a handle to store our settings in, move the settings to that handle,
  293. // and then store it to disk as a resource. Also store the folder alias as a
  294. // resource.  If we can do this without any errors occurring, update our
  295. // global flags.
  296.  
  297.     spoolCollHdl = (SpoolCollection**) NewHandle(sizeof(SpoolCollection));
  298.     nrequire((err = MemError()), NewHandle_Failed);
  299.  
  300.     BlockMove(&spoolConfig, *spoolCollHdl, sizeof(SpoolCollection));
  301.     err = ReplaceResource((Handle) spoolCollHdl, kConfigType, kConfigID);
  302.     DisposHandle((Handle) spoolCollHdl);
  303.     nrequire(err, ReplaceResource_Failed);
  304.  
  305.     err = ReplaceResource((Handle) gCurFolderAlias, rAliasType, kAliasID);
  306.     nrequire(err, ReplaceResource_Failed);
  307.     gSettingsChanged = false;
  308.     gLastOnOffSetting = spoolConfig.extTurnedOn;
  309.  
  310.  
  311. ReplaceResource_Failed:
  312. NewHandle_Failed:
  313.  
  314.     UseResFile(oldResFile);
  315.     CloseResFile(resRefNum);
  316.  
  317.  
  318. OpenPrefsFile_Failed:
  319. SettingsHaveNotChanged:
  320. GetSpoolCollection_Failed:
  321.  
  322.     return err;
  323. }
  324.  
  325.  
  326. /*******************************************************************
  327.     SPCreateSpoolFile is our override for GXCreateSpoolFile.  It
  328.     just gets our collection item, sees if we're supposed to do
  329.     anything, and if so, changes where we create the spool file
  330.     before forwarding the message.
  331.     
  332.     If we get an error, we ignore it and try to create the spool
  333.     file without redirection.  This is so we're more user-friendly
  334.     in the case of a glitch.  At least their file will be created.
  335.  
  336. ********************************************************************/
  337.  
  338. OSErr SPCreateSpoolFile(FSSpec * anFSSpec, long createOptions, gxSpoolFile *spFile)    
  339. {
  340.     OSErr                    err;
  341.     SpoolCollection            **spoolCollHdl;
  342.     short                    oldResFile, resRefNum;
  343.     AliasHandle                folderAlias;
  344.     FSSpec                    theFSSpec;
  345.     Boolean                    wasChanged;
  346.  
  347. // Get and open our preferences file's resoruce fork.
  348.  
  349.     oldResFile = CurResFile();
  350.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  351.     nrequire(err, OpenPrefsFile_Failed);
  352.     UseResFile(resRefNum);
  353.  
  354.  
  355. // Load the folder alias and extension settings we last saved.
  356.  
  357.     folderAlias = (AliasHandle) Get1Resource(rAliasType, kAliasID);
  358.     nrequire((err = ResError()), CouldNotLoadAlias);
  359.     DetachResource((Handle) folderAlias);
  360.  
  361.     spoolCollHdl = (SpoolCollection **) Get1Resource(kConfigType, kConfigID);
  362.     nrequire((err = ResError()), CouldNotLoadSettings);
  363.  
  364.  
  365. // If the user had usd turned on, try to resolve the alias.  If it gets updated,
  366. // save the new alias to disk.
  367.  
  368.     if ((*spoolCollHdl)->extTurnedOn)
  369.     {
  370.         err = ResolveAlias(nil, folderAlias, &theFSSpec, &wasChanged);
  371.         nrequire(err, ResolveAlias_Failed);
  372.  
  373.         if (wasChanged)
  374.             err = ReplaceResource((Handle) folderAlias, rAliasType, kAliasID);
  375.  
  376.         nrequire(err, ReplaceResource_Failed);
  377.         FSMakeFSSpec(theFSSpec.vRefNum, GetActualDirID(&theFSSpec), anFSSpec->name, &theFSSpec);
  378.         BlockMove(&theFSSpec, anFSSpec, sizeof(FSSpec));
  379.     }
  380.  
  381.  
  382. // Release the settings resource, dispose of the alias handle, reset our current
  383. // resource file, close the prefs file, and forward the message down the chain.
  384.  
  385. ReplaceResource_Failed:
  386. ResolveAlias_Failed:
  387.     
  388.     ReleaseResource((Handle) spoolCollHdl);
  389.  
  390.  
  391. CouldNotLoadSettings:
  392.     
  393.     DisposHandle((Handle) folderAlias);
  394.  
  395.  
  396. CouldNotLoadAlias:
  397.  
  398.     UseResFile(oldResFile);
  399.     CloseResFile(resRefNum);
  400.  
  401.  
  402. OpenPrefsFile_Failed:
  403.  
  404.     return Forward_GXCreateSpoolFile(anFSSpec, createOptions, spFile);
  405. }
  406.  
  407.  
  408. /*******************************************************************
  409.     GetFolder is a replacement for StandardGetFile which allows
  410.     us to select directories.
  411.  
  412. ********************************************************************/
  413.  
  414. Boolean GetFolder(FSSpec *fSpec)
  415. {
  416.     OSErr                err;
  417.     Point                where = {-1,-1};
  418.     StandardFileReply    sfReply;
  419.     short                foundVRefNum;
  420.     long                foundDirID;
  421.  
  422. // Have the user select a folder using the GetFile dialog.
  423.  
  424.     CustomGetFile((FileFilterYDProcPtr) FilterAllFiles, -1, nil, &sfReply, r_fileDlogID,
  425.                   where, MyDlgHook, nil, nil, nil, &sfReply);
  426.  
  427.     require(sfReply.sfGood, UserCancelledDialog);
  428.  
  429.  
  430. // If the user selected a volume, use the boot volume's desktop folder instead.
  431.  
  432.     nrequire(sfReply.sfIsVolume, UserDidNotChooseVolume);
  433.  
  434.     err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  435.     nrequire(err, FindFolder_Failed);
  436.  
  437.     err = FSMakeFSSpec(foundVRefNum, foundDirID, nil, fSpec);
  438.  
  439.  
  440. UserDidNotChooseVolume:
  441.     
  442.     err = FSMakeFSSpec(sfReply.sfFile.vRefNum, sfReply.sfFile.parID, nil, fSpec);
  443.  
  444.  
  445. // There appears to be a bug in the CustomGetFile stuff.  Sometimes the directory
  446. // returned is bogus.  It seems to only happen when the desktop folder should be
  447. // selected.  MPW demonstrates this same bug, so I don't think it's my code.
  448. //
  449. // Test: using a system with more than one volume attached, use MPW's "Set Directory"
  450. // dialog to highlight the desktop icon of a drive other than the boot drive.  Now
  451. // hit the "Select Current Directory" button.  All's well.  Go back and highlight a
  452. // folder alias that belongs to a DIFFERENT volume (or highlight nothing at all) at
  453. // the desktop folder level, and select that.  You get an error. (-120, "directory
  454. // not found").  That's what I was seeing here too.
  455. //
  456. // It appears that the standard file stuff is looking for the desktop folder of the
  457. // wrong volume.  As a work-around, I simply return the boot drive's desktop folder
  458. // if I get a "directory not found" error.
  459.  
  460.     if (err == dirNFErr)
  461.     {
  462.         err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  463.         nrequire(err, FindFolder_Failed);
  464.  
  465.         err = FSMakeFSSpec(foundVRefNum, foundDirID, nil, fSpec);
  466.     }
  467.  
  468.  
  469. FindFolder_Failed:
  470.  
  471.     if (err) sfReply.sfGood = false;
  472.  
  473.  
  474. UserCancelledDialog:
  475.  
  476.     return sfReply.sfGood;
  477. }
  478.  
  479.  
  480. /*******************************************************************
  481.     MyDlgHook is a dialog hook routine for CustomGetFile.
  482.  
  483. ********************************************************************/
  484.  
  485. pascal short MyDlgHook(short item, DialogPtr theDlg, Ptr userData)
  486. {
  487.     StandardFileReply    *curReply;
  488.     OSType                refCon;
  489.  
  490. // Unless we're being called from the main SF dialog, we don't do
  491. // anything.
  492.  
  493.     refCon = GetWRefCon(theDlg);
  494.     require((refCon == sfMainDialogRefCon), NotMainSFDialog);
  495.  
  496.  
  497. // If the user has selected the volume icon, change to the
  498. // desktop folder.  If they've selected our "Select Current Folder"
  499. // button, "OK" the dialog.
  500.  
  501.     curReply = (StandardFileReply *) userData;
  502.  
  503.     switch (item)
  504.     {
  505.         case sfItemVolumeUser:
  506.             curReply->sfFile.name[0] = '\0';
  507.             curReply->sfFile.parID = 2;
  508.             curReply->sfIsFolder = false;
  509.             curReply->sfIsVolume = false;
  510.             curReply->sfFlags = 0;
  511.             item = sfHookChangeSelection;
  512.             break;
  513.         
  514.         case d_selectItem:
  515.             item = sfItemOpenButton;
  516.             break;
  517.     }
  518.  
  519.  
  520. NotMainSFDialog:
  521.  
  522.     return item;
  523. }
  524.  
  525.  
  526. /*******************************************************************
  527.     FilterAllFiles is our CustomGetFile filterProc that only lets
  528.     directories be added to the display list.
  529.  
  530. ********************************************************************/
  531.  
  532. pascal Boolean FilterAllFiles(CInfoPBPtr pb, Ptr myDataPtr)
  533. {
  534. #pragma unused(myDataPtr);
  535.  
  536.      return (Boolean) !(pb->hFileInfo.ioFlAttrib & (1 <<4));
  537. }
  538.  
  539.  
  540. /*******************************************************************
  541.     GetActualDirID returns that actual directory ID (not the
  542.     parent's) of the folder referenced in the FSSpec * passed.
  543.  
  544. ********************************************************************/
  545.  
  546. long GetActualDirID(FSSpec * fSpec)
  547. {
  548.     DirInfo    infoPB;
  549.  
  550. // Get the passed folder's directory ID.
  551.  
  552.     infoPB.ioNamePtr = fSpec->name;
  553.     infoPB.ioVRefNum = fSpec->vRefNum;
  554.     infoPB.ioDrDirID = fSpec->parID;
  555.     infoPB.ioFDirIndex = 0;
  556.     PBGetCatInfo((CInfoPBPtr) &infoPB,false);
  557.  
  558.     return infoPB.ioDrDirID;
  559. }
  560.  
  561.  
  562. /*******************************************************************
  563.     GetDefaultSettings returns our previously saved settings.
  564.  
  565. ********************************************************************/
  566.  
  567. OSErr GetDefaultSettings(SpoolCollection *spoolConfig)
  568. {
  569.     OSErr                err;
  570.     long                foundDirID;
  571.     short                foundVRefNum, oldResRef, resRefNum;
  572.     SpoolCollection        **spoolCollHdl = nil;
  573.     FInfo                theFInfo;
  574.     FSSpec                curFSpec;
  575.     Boolean                wasChanged, isNew = false;
  576.     Str255                prefsFileName;
  577.  
  578. // Try to find our prefs file in the Preferences folder.
  579.     
  580.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  581.     require((err == fnfErr), PrefsFileExists);
  582.  
  583. // No prefs file-- create and open one.
  584.  
  585.     GetPrefsName(&prefsFileName);
  586.     err = FindFolder(kOnSystemDisk,kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  587.     nrequire(err, FindFolder_Failed1);
  588.  
  589.     HDelete(foundVRefNum, foundDirID, prefsFileName);
  590.     HCreateResFile(foundVRefNum, foundDirID, prefsFileName);
  591.     nrequire((err = ResError()), HCreateResFile_Failed);
  592.     
  593.     HGetFInfo(foundVRefNum, foundDirID, prefsFileName, &theFInfo);
  594.     theFInfo.fdType = kPrefsFileType;
  595.     theFInfo.fdCreator = kPrefsFileCreator;
  596.     HSetFInfo(foundVRefNum, foundDirID, prefsFileName, &theFInfo);
  597.  
  598.     resRefNum = HOpenResFile(foundVRefNum, foundDirID, prefsFileName, fsRdWrPerm); 
  599.     nrequire((err = ResError()), HOpenResFile_Failed);
  600.  
  601.  
  602. PrefsFileExists:
  603.  
  604.     oldResRef = CurResFile();
  605.     UseResFile(resRefNum);
  606.  
  607.  
  608. // Get a previously saved or newly created alias to the spool folder.
  609.  
  610.     if (gCurFolderAlias != nil)
  611.         DisposHandle((Handle) gCurFolderAlias);
  612.  
  613.     gCurFolderAlias = (AliasHandle) Get1Resource(rAliasType, kAliasID);
  614.  
  615.     if (gCurFolderAlias != nil)
  616.         DetachResource((Handle) gCurFolderAlias);
  617.     else                                        // no resource-- create one.
  618.     {
  619.         err = FindFolder(kOnSystemDisk, kPrintMonitorDocsFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  620.         nrequire(err, FindFolder_Failed2);
  621.  
  622.         FSMakeFSSpec(foundVRefNum, foundDirID, nil, &curFSpec);
  623.         err = NewAlias(nil, &curFSpec, &gCurFolderAlias);
  624.         nrequire(err, NewAlias_Failed);
  625.  
  626.         err = ReplaceResource((Handle) gCurFolderAlias, rAliasType, kAliasID);
  627.         nrequire(err, ReplaceResource_Failed);
  628.     }
  629.  
  630.  
  631. // Get a previously saved or newly created handle to our panel settings.
  632.  
  633.     spoolCollHdl = (SpoolCollection **) Get1Resource(kConfigType, kConfigID);
  634.  
  635.     if (spoolCollHdl != nil)
  636.         DetachResource((Handle) spoolCollHdl);
  637.     else                                        // no resource-- create one.
  638.     {
  639.         spoolCollHdl = (SpoolCollection **) NewHandle(sizeof(SpoolCollection));
  640.         (*spoolCollHdl)->extTurnedOn = kRedirectDisabled;
  641.         isNew = true;
  642.     }
  643.  
  644.  
  645. // If our alias needs to be updated, update it and save off the new volume
  646. // and folder names.  In any case, move our settings into the passed pointer
  647. // and return them to the caller.
  648.  
  649.     require_action((spoolCollHdl != nil), CanNotCreateSettings, err = MemError(););
  650.  
  651.     HLock((Handle) spoolCollHdl);
  652.     wasChanged = AliasToPathName(gCurFolderAlias, &(*spoolCollHdl)->folderName, &(*spoolCollHdl)->volumeName);
  653.     HUnlock((Handle) spoolCollHdl);
  654.  
  655.     if (wasChanged || isNew)
  656.         err = ReplaceResource((Handle) spoolCollHdl, kConfigType, kConfigID);
  657.  
  658.     BlockMove(*spoolCollHdl, spoolConfig, sizeof(SpoolCollection));
  659.     DisposHandle((Handle) spoolCollHdl);
  660.  
  661.  
  662. CanNotCreateSettings:
  663. ReplaceResource_Failed:
  664. NewAlias_Failed:
  665. FindFolder_Failed2:
  666.  
  667.     UseResFile(oldResRef);
  668.     CloseResFile(resRefNum);
  669.  
  670.  
  671. HOpenResFile_Failed:
  672. HCreateResFile_Failed:
  673. FindFolder_Failed1:
  674.     
  675.     return err;
  676. }
  677.  
  678.  
  679. /*******************************************************************
  680.     OpenPrefsFile opens our preferences file.
  681.  
  682. ********************************************************************/
  683.  
  684. OSErr OpenPrefsFile(short *resRefNum, char permission)
  685. {
  686.     OSErr    err;
  687.     long    foundDirID;
  688.     short    foundVRefNum;
  689.     Boolean    targetIsFolder, wasAliased;
  690.     FSSpec    fSpec;
  691.     Str255    prefsFileName;
  692.  
  693.  
  694. // Find the preferences folder and open our preferences file, if it exists.
  695.  
  696.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  697.  
  698.     if (!err)
  699.     {
  700.         GetPrefsName(&prefsFileName);
  701.         err = FSMakeFSSpec(foundVRefNum, foundDirID, prefsFileName, &fSpec);
  702.         nrequire(err, ThereWasAnError);
  703.  
  704.         err = ResolveAliasFile(&fSpec, true, &targetIsFolder, &wasAliased);
  705.         nrequire(err, ThereWasAnError);
  706.  
  707.         gPrefsVRefNum = fSpec.vRefNum;
  708.         *resRefNum = FSpOpenResFile(&fSpec, permission);
  709.         err = ResError();
  710.     }
  711.  
  712.  
  713. ThereWasAnError:
  714.  
  715.     return err;
  716. }
  717.  
  718.  
  719. /*******************************************************************
  720.     ReplaceResource adds or replaces a resource in the current
  721.     resource file.  (Make sure the file you want to access is
  722.     open and is the current resource file!)  The resource is
  723.     detached when the data is returned, so call DisposHandle on
  724.     it, NOT ReleaseResource.
  725.  
  726. ********************************************************************/
  727.  
  728. OSErr ReplaceResource(Handle newData, OSType resourceType, short resourceID)
  729. {
  730.     OSErr    err;
  731.     Handle    oldResource;
  732.  
  733.     oldResource = Get1Resource(resourceType, resourceID);
  734.     
  735.     require((oldResource != nil), AddingNewResource);
  736.     RmveResource(oldResource);
  737.     DisposHandle(oldResource);
  738.  
  739.  
  740. AddingNewResource:
  741.     
  742.     AddResource(newData, resourceType, resourceID, "\p");
  743.     
  744.     if (!(err = ResError()))
  745.         WriteResource(newData);
  746.  
  747.     UpdateResFile(CurResFile());
  748.     FlushVol(nil, gPrefsVRefNum);
  749.  
  750.     if (!err) DetachResource(newData);
  751.     return err;
  752. }
  753.  
  754.  
  755. /*******************************************************************
  756.     GetSpoolCollection is a handy routine that retrieves our
  757.     SpoolCollection item from the current job collection.
  758.  
  759. ********************************************************************/
  760.  
  761. OSErr GetSpoolCollection(SpoolCollection *spoolCollect)
  762. {
  763.     OSErr        err;
  764.     Collection    jobCollection;
  765.  
  766.     jobCollection = GXGetJobCollection(GXGetJob());
  767.  
  768.     err = GetCollectionItem(jobCollection,
  769.                             kSpoolCollectionType,
  770.                             gxPrintingTagID,
  771.                             nil,
  772.                             spoolCollect);
  773.     return err;
  774. }
  775.  
  776.  
  777. /*******************************************************************
  778.     StoreSpoolCollection is a handy routine that stores our
  779.     SpoolCollection item in the current job collection.
  780.  
  781. ********************************************************************/
  782.  
  783. OSErr StoreSpoolCollection(SpoolCollection *spoolCollect, Boolean newItem)
  784. {
  785.     OSErr        err;
  786.     Collection    jobCollection;
  787.     long        index, itemSize, attributes;
  788.  
  789.     jobCollection = GXGetJobCollection(GXGetJob());
  790.  
  791.     if (newItem)
  792.         err = AddCollectionItem(jobCollection,
  793.                                 kSpoolCollectionType,
  794.                                 gxPrintingTagID,
  795.                                 sizeof(SpoolCollection),
  796.                                 spoolCollect);
  797.     else
  798.     {
  799.         err = GetCollectionItemInfo(jobCollection,
  800.                                     kSpoolCollectionType,
  801.                                     gxPrintingTagID,
  802.                                     &index,
  803.                                     &itemSize,
  804.                                     &attributes);
  805.  
  806.         if (!err)
  807.             err = ReplaceIndexedCollectionItem(jobCollection,
  808.                                                index,
  809.                                                sizeof(SpoolCollection),
  810.                                                spoolCollect);
  811.     }
  812.  
  813.     return err;
  814. }
  815.  
  816.  
  817. /*******************************************************************
  818.     AliasToPathName extracts the folder and volume name from the
  819.     passed alias, updating them if it needs to.  (I use MatchAlias
  820.     so that I can avoid the user dialogs and volume mounting if
  821.     the alias spans a network.)  If these types of aliases are
  822.     out of date, they will be updated at CreateSpoolFile time.
  823.     We return true if the alias has been updated and needs to be
  824.     saved off.
  825.  
  826. ********************************************************************/
  827.  
  828. Boolean AliasToPathName(AliasHandle anAlias, char *folderName, char *volName)
  829. {
  830.     Boolean        wasChanged;
  831.     OSErr        err;
  832.     FSSpec        fSpec;
  833.     short        num = 1;
  834.  
  835. // See if we can find the folder referenced by the alias.
  836.  
  837.     err = MatchAlias(nil, kARMSearch, anAlias, &num, (FSSpecArrayPtr) &fSpec,
  838.                      &wasChanged, nil, nil);
  839.  
  840.     nrequire_action(err, AliasMgrError, wasChanged = false;);
  841.  
  842.  
  843. // If we found the folder but its path has changed, update the folder and
  844. // volume names referenced.
  845.  
  846.     if (wasChanged)
  847.         err = UpdateAlias(nil, &fSpec, anAlias, &wasChanged);
  848.  
  849.     nrequire(err, AliasMgrError);
  850.     GetAliasInfo(anAlias, asiAliasName, folderName);
  851.     GetAliasInfo(anAlias, asiVolumeName, volName);
  852.  
  853.  
  854. AliasMgrError:
  855.  
  856.     return wasChanged;
  857. }
  858.  
  859.  
  860. /*******************************************************************
  861.     GetPrefsName returns the name of our preferences file.
  862.  
  863. ********************************************************************/
  864.  
  865. void GetPrefsName(char *prefsName)
  866. {
  867.     short    oldResFile;
  868.  
  869. // We just load the name of our preferences file from our extension.
  870.  
  871.     oldResFile = CurResFile();
  872.     UseResFile(GXGetMessageHandlerResFile());
  873.  
  874.     GetIndString(prefsName, r_stringRsrc, r_prefsStrIdx);
  875.     UseResFile(oldResFile);
  876. }
  877.  
  878.